setupdigetdeviceinterfacedetail (setupapi)
Last changed: -107.1.25.194

.
Summary

C# Signature:

[DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern Boolean SetupDiGetDeviceInterfaceDetail(
   IntPtr hDevInfo,
   ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
   ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
   UInt32 deviceInterfaceDetailDataSize,
   out UInt32 requiredSize,
   ref SP_DEVINFO_DATA deviceInfoData
);

VB Signature:

Public Declare Auto Function SetupDiGetDeviceInterfaceDetail2 Lib "setupapi.dll" Alias "SetupDiGetDeviceInterfaceDetailW" ( _
    ByVal hDevInfo As IntPtr, _
    ByRef deviceInterfaceData As SP_DEVICE_INTERFACE_DATA, _
    ByRef deviceInterfaceDetailData As SP_DEVICE_INTERFACE_DETAIL_DATA, _
    ByVal deviceInterfaceDetailDataSize As Int32, _
    ByRef requiredSize As Int32, _
    ByRef deviceInfoData As SP_DEVINFO_DATA) As Boolean

User-Defined Types:

    [StructLayout(LayoutKind.Sequential)]
    public struct SP_DEVINFO_DATA
    {
        public UInt32 cbSize;
        public Guid ClassGuid;
        public UInt32 DevInst;
        public IntPtr Reserved;
    }

    // Device interface data
    [StructLayout(LayoutKind.Sequential)]
    public struct SP_DEVICE_INTERFACE_DATA
    {
        public UInt32 cbSize;
        public Guid InterfaceClassGuid;
        public UInt32 Flags;
        public UIntPtr Reserved;
    }

    // Device interface detail data
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct SP_DEVICE_INTERFACE_DETAIL_DATA
    {
        public UInt32 cbSize;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string DevicePath;
    }

VB User-Defined Types:

<StructLayout(LayoutKind.Sequential)> _
Public Structure SP_DEVICE_INTERFACE_DATA
    Public cbSize As Int32
    Public InterfaceClassGuid As Guid
    Public Flags As Int32
    Public Reserved As IntPtr
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Public Structure SP_DEVICE_INTERFACE_DETAIL_DATA
    Public cbSize As UInt32
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> _
    Public DevicePath As String
End Structure

<StructLayout(LayoutKind.Sequential)> _
Public Structure SP_DEVINFO_DATA
    Public cbSize As UInteger
    Public ClassGuid As Guid
    Public DevInst As UInteger
    Public Reserved As IntPtr
End Structure

Alternative Managed API:

Do you know one? Please contribute it!

Notes:

SP_DEVICE_INTERFACE_DETAIL_DATA needs a ByVal TCHAR[ANYSIZE_ARRAY] member, which could conceivably be any size. This does not bode well for determining the correct size, however DevicePath should be no longer than MAX_PATH (256).

Tips & Tricks:

On the VB side i could not get a valid result passing nulls to the optional parameters, but passing initialized structures seems to work fine.

Sample Code:

Guid DiskGUID = new Guid(GUID_DEVINTERFACE_DISK);

// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a disk
IntPtr h = SetupDiGetClassDevs(ref DiskGUID, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (h.ToInt32() != INVALID_HANDLE_VALUE)
{
   bool Success = true;
   int i = 0;
   while (Success)
   {
     // create a Device Interface Data structure
     SP_DEVICE_INTERFACE_DATA dia = new SP_DEVICE_INTERFACE_DATA();
     dia.cbSize = Marshal.SizeOf(dia);

     // start the enumeration
     Success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref DiskGUID, i, ref dia);
     if (Success)
     {
       // build a DevInfo Data structure
       SP_DEVINFO_DATA da = new SP_DEVINFO_DATA();
       da.cbSize = Marshal.SizeOf(da);

       // build a Device Interface Detail Data structure
       SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA();
       if (IntPtr.Size == 8) // for 64 bit operating systems
       didd.cbSize = 8;
       else
       didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // for 32 bit systems

       // now we can get some more detailed information
       int nRequiredSize = 0;
       int nBytes = BUFFER_SIZE;
       if (SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, nBytes, ref nRequiredSize, ref da))
       {
         // current InstanceID is at the "USBSTOR" level, so we
         // need up "move up" one level to get to the "USB" level
         IntPtr ptrPrevious;
         CM_Get_Parent(out ptrPrevious, da.DevInst, 0);

         // Now we get the InstanceID of the USB level device
         IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes);
         CM_Get_Device_ID(ptrPrevious, ptrInstanceBuf, nBytes, 0);
         string InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf);

         Marshal.FreeHGlobal(ptrInstanceBuf);
       }
     }
   }
   i++;
}
SetupDiDestroyDeviceInfoList(h);

Documentation